package com.fas.system.websocket;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fas.common.constant.Constants;
import com.fas.common.utils.spring.SpringUtils;
import com.fas.system.domain.vo.EquipmentAlarmVo;
import com.fas.system.service.IFasEquipmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ServerEndpoint
 * 使用springboot的唯一区别是要@Component声明下，而使用独立容器是由容器自己管理websocket的，但在springboot中连容器都是spring管理的。
 * 虽然@Component默认是单例模式的，但springboot还是会为每个websocket连接初始化一个bean，所以可以用一个静态set保存起来。
 */
@ServerEndpoint("/websocket/server/{userId}") //WebSocket客户端建立连接的地址
@Component
public class Websocket {

    private final static Logger logger = LoggerFactory.getLogger(Websocket.class);
    /**
     * 存活的session集合（使用线程安全的map保存）
     */
    public static Map<Integer, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 建立连接的回调方法
     *
     * @param session 与客户端的WebSocket连接会话
     * @param userId  用户信息token，WebSocket支持路径参数
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Integer userId) {
//        String sessionId = session.getId();
        logger.debug("用户{}已上线", userId);
        sessionMap.put(userId, session);
        dbOfflineMsgSend(userId);
    }

    /**
     * 收到客户端消息的回调方法
     *
     * @param message 客户端传过来的消息
     * @param session 对应的session
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println(message);
        session.getBasicRemote().sendText("收到");
    }

    /**
     * 发生错误的回调方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("socket 发生错误 error:{}", error.toString());
    }

    /**
     * 关闭连接的回调方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") Integer userId) {
        logger.debug("用户{}已下线", userId);
        sessionMap.remove(userId);
    }

    public void sendMessage(String message, List<Integer> userIds) {
        try {
            if (sessionMap.size() > 0) {
                for (Map.Entry<Integer, Session> map : sessionMap.entrySet()) {
                    if (userIds.contains(map.getKey())) {//用户名存在于session中
                        map.getValue().getBasicRemote().sendText(message);
                    }
                }
            } else {
                logger.debug("当前无维保单位人员在线,信息推送的内容为：" + message);
            }
        } catch (IOException e) {
            logger.error("消息推送失败");
            e.printStackTrace();
        }
    }

    /**
     * 用户每次连接，判断是否存在离线消息,从数据库获取
     */
    private void dbOfflineMsgSend(Integer userId) {
        logger.debug("websocket已打开，查询离线消息并推送");
        List<EquipmentAlarmVo> equipmentAlarmVos = SpringUtils.getBean(IFasEquipmentService.class).unreadList(userId);
        // 设备存在未读消息，
        // TODO 维保单位和工程师的工单消息
        if (equipmentAlarmVos != null && equipmentAlarmVos.size() > 0) {
            PushData<List<EquipmentAlarmVo>> listPushData = new PushData<>(Constants.EQUIPMENT, equipmentAlarmVos);
            String msg = JSONObject.toJSONString(listPushData, SerializerFeature.WriteMapNullValue);
            this.sendMessage(msg, Collections.singletonList(userId));
            logger.debug("有离线消息，已推送成功");
        }else{
            logger.debug("沒有离线消息");
        }
    }
}